perm filename CLASSE.TXT[CLS,LSP] blob
sn#826006 filedate 1986-10-10 generic text, type T, neo UTF8
This draft document describes ideas now being discussed in a working group
whose goal is to define a standard for object-oriented programming in
Common Lisp. This draft document was written by two members of the working
group, and represents our best efforts at a faithful transcript of
conclusions reached by the group to date. Throughout this document we flag
some subjects as "still under discussion" or as possible extensions to the
standard. This draft is not yet a complete or finished proposal for a
standard.
For the present, we are using the name "Classes" to refer to the new
standard. It is important to have a name to use now, so we can clearly
distinguish between the new standard and the existing object-oriented
systems, such as Flavors and CommonLoops. defclass is the mechanism for
defining new classes, so Classes seems a natural name for the tools and
ideas that comprise the object-oriented programming system. Classes fits
nicely with other chapters of Common Lisp the Language, perhaps best placed
directly after the "Structures" chapter.
This draft document is in the public domain.
1. STATEMENT OF GOALS
History and Motivation
There is currently substantial experience with object-oriented programming
paradigms within a variety of contexts. The two groups that have the most
experience in this area are Symbolics and Xerox.
Symbolics has experience with Flavors, an important object-oriented
programming extension to Lisp. In 1985, Symbolics undertook a redesign of
Flavors, which resulted in New Flavors, a compatible extension to the
previous system.
Xerox has experience with object-oriented programming, both in Lisp and in
the Smalltalk tradition. In 1985 Xerox began to design a new
object-oriented programming extension to Common Lisp, called CommonLoops.
CommonLoops and Flavors have important similarities.
Many people in the Common Lisp community now see a need for defining a
standard for object-oriented programming that would be part of Common Lisp.
The primary benefit of such a standard would be the ability to write
portable Common Lisp code in an object-oriented style.
At the Common Lisp committee meeting in Boston in December 1985, many
vendors of application software made it clear how important such a standard
is to them. Again at the ACM Conference on Lisp and Functional Programming
in Cambridge in August 1986, the same point was made.
After the ACM Conference there was a meeting of the Common Lisp
Object-oriented Programming Committee. A clear consensus was reached that
work should begin immediately to define a standard. Representatives from
Xerox, Lucid, Symbolics, LMI, HP, Sun, and the Japanese CommonLisp
committee supported this consensus.
A working group composed of representatives from Xerox and Symbolics met
after the ACM Lisp Conference. At that meeting we reached agreement on
several general goals:
1
The standard will not be CommonLoops, nor will it be Flavors; however it
will incorporate the most useful features from both. For example, it will
include multi-methods from CommonLoops and declarative method combination
from Flavors.
Goals for Classes
Some high-level goals were stated and agreed upon:
Sound basis
Classes will include only those aspects of object-oriented
programming that are well-understood at this time, and omit ideas
that are still subjects for research.
Flexibility
Classes will offer a flexible framework for designers to implement
different schemes. This encourages exploration of different styles
of object-oriented programming.
Power Classes will offer the basic tools for writing programs in an
object-oriented style, and will be powerful enough to meet the
needs of most programs. Programmers need not write extensions to
Classes to do straightforward object-oriented programming.
Compatibility
It should be a convenient and simple procedure to translate
programs written in CommonLoops or New Flavors to the new standard.
We intend to provide tools that will perform the translation
automatically.
Implementation
Classes will allow efficient implementation on stock hardware as
well as specialized machines.
Features to be Included in Classes
We intend to include the following features in Classes. This is not an
exhaustive list, but is intended to communicate some of the areas of
agreement that have been reached.
2
Generic functions
Classes will use the normal Lisp function-calling syntax.
Class and Type Space Merging
Every object in the Lisp system has a class and hence can be used
to select methods.
Dispatching
Classes will allow for several types of dispatching, including
classical methods (which dispatch on a single object) and
multi-methods (which dispatch on more than one object).
Multiple inheritance
Classes can be combined together freely; they need not fit into a
rigid hierarchy.
Meta-objects
Classes defines objects for the major implementation entities of
the system to allow for extensibility. Classes, methods, and
generic functions all have corresponding objects, organized by
well-defined, documented protocols.
Declarative Method Combination
Method selection and combination can be controlled declaratively.
Users can define new method combination paradigms.
3
2. TERMINOLOGY
This section defines the terminology used in this document.
Object Any Lisp datum, such as a number, a character, or an instance.
Class An object that describes the structure and behavior of a set of
objects. All Lisp objects have a class, and can be queried for
the class by using the class-of function.
Instance Every object is an instance of some class. The term instance is
used to describe the instance/class relationship of an object to
its class.
Metaclass Each object has a class. A class itself is an object which has a
class. The metaclass of an object is the class of the class of
the object.
A metaclass itself is an object, which has a class. The term
metaclass is used to refer to a class that is suitable for being
the class of a class. An instance of a metaclass is a class.
Built-in class
A built-in class corresponds to one of the predefined Common Lisp
type specifiers; for example array is a built-in class for
objects of type array. The metaclass of an array is
built-in-class. You cannot define new built-in classes, nor use
them in the definition of a new class.
Super-class
The super-classes of a class are those classes that are included
explicitly in the super-class list in the defclass form. Each
super-class is an integral part of a class. The class inherits
characteristics (such as slots) and behavior (such as methods)
from each of its super-classes. This is the primary mechanism
for program modularity. A typical mode of use is to define
several basic classes and combine them to achieve specialized
behavior.
4
Components
The components of a class include: the class itself, the
super-classes of the class, the super-classes of the
super-classes of the class, and so on.
Slots Slots define the structure of instances of the class, as do
defstruct slots. When defining a new class, you specify the
slots of the class. An :instance slot is a place where you can
store data inside an instance. This is the most commonly used
kind of slot, where each instance has an individual slot of the
same name. A :class slot is a place where you can store data
inside a class. There is only one slot, whose value is shared by
all instances of the class.
Accessor function
A generic function that enables you to read the value of a slot.
setf may be used with an accessor function to write the value of
the slot.
Reader function
A generic function that enables you to read the value of a slot.
setf may not be used with a reader function.
Generic Function
A function that has methods defined for it. Ordinary functions
have a single definition; generic functions have a distributed
definition. The implementation of an ordinary function is the
same whenever the function is called. The implementation of a
generic function varies from call to call, depending on the
classes of its arguments.
Method An object that describes how to perform a generic function for a
given set of arguments. One or more methods are chosen according
to the classes of the arguments to the generic function; the
choosing of the method or methods is called dispatching.
Classical method
The dispatching is based on the class of the first argument to
the generic function (that is, on the class of a single object).
5
Multi-method
The dispatching is based on two or more arguments to the generic
function (that is, on the class of several objects).
Default method
A default method does not specify any type for discrimination
purposes, or specifies the type as t. A default method is
selected when no more specific method for the generic function is
defined.
SETF method
A method for a setf generic function. A setf generic function is
the function that is called when you evaluate an expression such
as: (SETF (G ...) VALUE), where G is any generic function. One
example of G is a slot accessor.
6
3. PROGRAMMER INTERFACE
This section describes the basic tools for defining classes, methods,
generic functions, and method combination types, and for making instances
of classes. The conventions used here follow the rules described in Common
Lisp the Language, section 1.2.5.
3.1 Defining Classes
defclass enables you to invent a new class, which is a template of a new
data type with named slots. defclass syntax allows for providing default
initialization of the slots, and for requesting that functions be
automatically generated to read and write the values of the slots, or to
construct new instances.
When you define a class you are also defining a type. (typep object
class-name) is true if object is an instance of that class.
See the section "Motivation for Several DEFCLASS Defaults", page 45.
The syntax of defclass is:
(defclass class-name ({super-class-name}*) ({slot-spec}*)
{option-name | (option-name {argument}*)}*)
For example:
(defclass ship ()
(name
(x-velocity :accessor ship-x-vel)
(y-velocity :accessor ship-y-vel)
(mass :reader ship-mass))
:initable-slots
(:default-init-plist :x-velocity 0 :y-velocity 0))
7
class-name is a non-null symbol that names the class being defined. If a
class is already defined with this name, this definition replaces the old
definition.
When the definition of a class changes, each existing instance of the class
is updated to the new format the next time it is accessed. (Note that this
behavior might be categorized as an optional feature.)
Each slot-spec is one of:
slot-name a non-null symbol.
(slot-name default-value)
default-value is any form. When an instance is made and no
initial value for this slot is provided as an argument to
make-instance, the slot is initialized to the value of this
form. The default-value is evaluated every time it is
used.
(slot-name {slot-option-name slot-option-value}*)
The defined slot-option-names and their values are:
:default-value form
This an alternate way for providing a default initial
value form. The form is evaluated every time it is
used.
:accessor generic-function-name
Specifies that a method for a generic function named
generic-function-name be automatically generated, to
read the value of this slot. setf may be used with
generic-function-name to write the value of the slot.
generic-function-name must be a non-nil symbol.
:reader generic-function-name
Specifies that a method for a generic function named
generic-function-name be automatically generated, to
read the value of this slot. Note that setf may not
be used with generic-function-name to write the value
8
of this slot. generic-function-name must be a non-nil
symbol.
:initable keyword
Specifies a keyword to be used to specify an initial
value of this slot, when a new instance is being made.
keyword must be a non-nil symbol.
:allocation keyword
Specifies where storage is allocated for this slot.
The keyword can be one of these:
:instance
Storage is allocated in the instance itself; each
instance has its separate value for this slot.
This is the default.
:class
Storage is allocated in the class. Thus a single
value for this slot is shared by all instances.
The exact semantics of :class are still under
discussion. For example, how are subclasses
handled? Does each subclass have a separate copy
of this slot, or do all subclasses share the same
copy?
:dynamic
This option is still under discussion; it is not
yet clear whether it should be in the standard.
The purpose of this option is to specify:
Storage is allocated in the instance at the time
of the first use of the slot. If the slot is
initialized with a keyword argument to
make-instance, the slot is allocated then. If
the first access is a read, then storage is
allocated and the default value declared in the
defclass, if any, is stored in the slot and
returned. If the first access is a setf, then
storage is allocated and the value is stored in
9
the slot and returned. This option allows
infrequently-used slots to take storage only when
necessary.
:none
No storage is to be allocated; the slot should
not exist in instances of this class. This is
used to override inheritance of slots defined by
a super-class.
If no default value for a slot is specified either in the defclass or the
make-instance form, the initial value of the slot is undefined and it is an
error to reference it. This is the same behavior as a defvar with no
initial value form.
Each super-class-name is a non-null symbol naming a class to be included as
an integral part of this class. The class now being defined will inherit
characteristics (such as slots) and behavior (such as methods) from each of
its super-classes. Each super-class-name must refer to a user-defined
class, not a "built-in" class. A built-in class corresponds to one of the
predefined Common Lisp type specifiers; for example array is a built-in
class for objects of type array.
Each class has a "component classes tree" composed of all its
super-classes, and the super-classes of each of its super-classes, and so
on. This tree is used to compute the class precedence list of the class.
See the section "Determining the Class Precedence List", page 36.
Note the following rules of defclass:
- All the component classes of a class must be defined before it is
permitted to make an instance of the class.
- A class must be defined before it can be mentioned as an argument
specifier in a defmethod.
- All component classes of a class must be defined before it is
permitted to evaluate or compile a with-slots that uses this
class.
10
Some implementations might add other options to defclass. Therefore it is
required that all implementations signal an error if they observe an option
that is not implemented locally.
Each option-name is a keyword that names an option; the arguments depend on
the option. The set of defined options and their arguments is:
(:accessor-prefix prefix {slot-name}*)
Causes a generic function to be generated automatically for
each indicated slot, or all slots if no slot-name is
present. Such a generic function is called an accessor; it
is used to read the value of a slot and can be used with
setf to write the value of a slot. The name of each
accessor is the prefix followed by slot-name. prefix is a
string or symbol.
(:reader-prefix prefix {slot-name}*)
Causes a generic function to be generated automatically for
each indicated slot, or all slots if no slot-name is
present. The generic function is called a reader; it is
used only to read the value of the slot and cannot be used
with setf to write the value of the slot. The name of each
reader is the prefix followed by slot-name. prefix is a
string or symbol.
(:default-init-plist {keyword argument}*)
Sets up defaults that are used when making an instance.
Each keyword must be a valid keyword argument to
make-instance. Each argument is a form that provides the
default value for that keyword argument if it is not
supplied. The user can override the defaults in the
:default-init-plist by providing keyword arguments to
make-instance. The arguments are evaluated every time they
are used.
(:initable-slots {slot-name}*)
Specifies that it is allowed to initialize each slot
indicated by slot-name, or all slots if no slot-names are
present. The initialization can be done by providing the
11
keyword with the same name as the slot (followed by the
slot's initial value) as an argument to make-instance, or
by including the keyword and value in the
:default-init-plist.
(:init-keywords {keyword}*)
This option is still under discussion; it is not yet clear
whether it should be in the standard. The purpose of this
option is to: Specify that each given keyword is an
allowed keyword argument to make-instance for this class.
There is no need to provide this argument to make it
possible to initialize slots; that is done with the
:initable-slots option. The :init-keywords option is used
in conjunction with methods you have written for
make-instance. If a make-instance method accepts an
argument, you must explicitly make it an allowed keyword
argument to make-instance by using this option.
(:documentation string)
Enables you to specify an arbitrary string as documentation
for this class. The form (documentation class-name
'type) retrieves this string.
(:constructor function-name [lambda-list])
Causes a constructor function to be generated
automatically. You can call that constructor to make a new
instance of the class. lambda-list describes what the
arguments to the constructor will be. The syntax of the
:constructor option to defclass is the same as that of the
defstruct :constructor option described in Common Lisp the
Language.
(:metaclass class-name)
Specifies that this class has a different implementation
than the system-provided default. If you have invented a
new metaclass, this option is the mechanism for creating
classes of that metaclass. class-name is the name of a
class that is capable of being the class of a class.
12
It is valid to specify more than one accessor, reader, and keyword for
initialization, for a single slot. This can happen when the :accessor slot
option is used to specify an accessor for a slot, and the :accessor-prefix
option is used to specify that all slots should have an accessor with the
supplied prefix.
If neither a reader nor an accessor is specified for a slot, the slot can
only be accessed by the function slot-value or with-slots using :direct t.
See the section "Accessing Slots Inside a Method: WITH-SLOTS", page 19.
See the section "Accessing Slots Directly: SLOT-VALUE", page 21.
3.2 Making a New Instance
make-instance is a generic function that makes and initializes a new
instance of a class.
(make-instance {class-name | class} {keyword argument}*)
The first argument must refer to a user-defined class. make-instance
cannot be used to make an instance of a built-in class.
The allowed keyword arguments depend on the defclass form. Any keyword
specified with the :initable, :initable-slots, or :init-keywords option is
allowed.
If a keyword and argument is given to make-instance, it overrides any
default specified for that keyword in the defclass form, such as the
:default-init-plist option, or a default value in a slot-spec.
Note that make-instance is a generic function for which you can write
methods. You can write methods to run after the new instance is created
and initialized. If a make-instance method accepts arguments, use the
:init-keywords option to make them allowed arguments to make-instance.
Otherwise use either (object &rest ignore) or (object &key
&allow-other-keys) as the lambda-list of the method.
13
3.3 Defining Methods and SETF Methods
defmethod defines a method for performing a generic function, when the
arguments to the generic function cause this method to be selected.
defmethod-setf defines a method for performing a setter function.
See the section "Why DEFMETHOD Disallows Method Selection on Optional
Parameters", page 49.
defmethod offers a general syntax which is appropriate for multi-methods,
and a classical syntax which is convenient for methods that dispatch on a
single argument. The classical syntax automatically makes the slots of the
first argument lexically available in the method. Multi-methods can use
with-slots to make slots of one or more objects lexically available in the
method.
General syntax:
(defmethod generic-function {option}*
qualified-lambda-list
{declaration | doc-string}*
{form}*)
(defmethod-setf generic-function {option}*
qualified-lambda-list
setf-lambda-list
{declaration | doc-string}*
{form}*)
Classical syntax:
(defmethod (generic-function self-class {option}*)
qualified-lambda-list
{declaration | doc-string}*
{form}*)
(defmethod-setf (generic-function self-class {option}*)
qualified-lambda-list
setf-lambda-list
{declaration | doc-string}*
14
{form}*)
The classical syntax is equivalent to the following expression in the
general syntax:
(defmethod generic-function {option}*
((self self-class) . qualified-lambda-list)
{declaration | doc-string}*
(with-slots (self)
{form}*))
The following examples illustrate the syntax of defmethod for different
types of methods, through the qualified-lambda-list} part of the
expression.
Multi-method, no options: (defmethod foo ((a c1) (b c2)) ...
Multi-method with option: (defmethod foo :before ((a c1) (b c2))...
Classical method, no options: (defmethod (foo c1) (b) ...
Classical method with option: (defmethod (foo c1 :before) (b) ...
Default method, no options: (defmethod foo (a b) ...
Default method with option: (defmethod foo :before (a b) ...
generic-function
A symbol that names the generic function being specialized.
self-class A symbol that names the class of the first argument.
option A non-nil symbol or a number, used by method combination to
identify the method. For example, :before or :after.
lambda-list A lambda list
qualified-lambda-list
A lambda-list as defined in Common Lisp the Language, page
60, possibly with some variables replaced by qualified-
variables so that method selection uses that argument. A
qualified-variable is a list such as: (variable-name
type-specifier). A qualified-variable may be used wherever
15
a lambda-list allows only a variable and not a list. Only
required parameters may be qualified. Note that the
qulaification on optional parameters is still under
discussion.
type-specifier type-specifier can be a user-defined class, or the name of
a structure defined by defstruct if the :type option was
not used, or a Common Lisp type specifier from the subset
listed here:
array integer rational
bit-vector list readtable
character long-float sequence
compiled-function null short-float
complex number single-float
cons package string
double-float pathname symbol
float random-state t
hash-table ratio vector
Note that the use of t as a type-specifier defines a
default method. Such a method is selected only when no
more specific method for the generic function is defined.
setf-lambda-list
Same as qualified-lambda-list except that for now there can
be only one parameter. In other words, setf-lambda-list is
a lambda-list containing exactly one required parameter,
which may be type-qualified. It is either (variable-name)
or ((variable-name type-specifier)).
16
3.4 Defining Generic Functions
defgeneric allows you to specify options and declarations that pertain to
the generic function as a whole. It is still under discussion whether it
should be required that a defgeneric be evaluated before any methods are
defined, or whether defmethod should set up a generic function with default
options, if no defgeneric has been evaluated.
(defgeneric generic-function lambda-list
{option | (option {argument}*)}*)
(defgeneric-setf generic-function lambda-list setf-lambda-list
{option | (option {argument}*)}*)
generic-function is a non-null symbol naming the generic function.
lambda-list is an ordinary lambda-list except no &aux variables are
allowed.
setf-lambda-list is (variable-name).
Each option is a keyword that names an option (or a documentation string).
The arguments depend on the option. The set of defined options and their
arguments is:
(:documentation string) | string
Associates a documentation string with the generic
function. The form (documentation generic-function-name
'function) retrieves this string.
(:method-combination name {argument}*)
Indicates that the generic function uses the type of method
combination named by name. The arguments depend on the
type of method combination. The most common argument is
one to specify the order of methods, which is either
:most-specific-first or :most-specific-last. The default
method combination type is :daemon, with the
:most-specific-first order.
17
(declare {declaration}+)
Declarations that apply to the whole function (as opposed
to declarations of variables) are permitted. The only
declaration in this standard is optimize, which can have
the value of speed or space. This allows the user to
control whether method selection is optimized for speed or
space; it has no affect on individual methods. Some
implementations might support other declarations. If an
implementation notices a declaration that it does not
support, it should follow the rules described on page 161
of Common Lisp the Language.
(:argument-precedence-order {parameter-name}+)
This option alters the default way of selecting methods.
By default, all type-qualified arguments are considered
from left to right; each type-qualified argument has
precedence over those to its right. When this option is
used, each type-qualified argument should be included as a
parameter-name, so the full precedence order is supplied.
(:interface {form}+)
Defines a function that runs instead of the generic
dispatch. This is completely transparent to anyone calling
the generic function. Such a prologue function can be used
to rearrange the arguments, to standardize the arguments
before the methods see them, to default optional arguments,
to do the shared non-generic portion of an operation, or
for any other purpose. Another use of this option is to
extend a generic function so it can also be used for
objects that are not instances of a built-in class.
Inside the forms you can trigger the generic dispatch with
(call-methods args...) or (apply-methods args...).
apply-methods treats the last argument as a list of
arguments just like apply. call-methods and apply-methods
are defined lexically only inside the :interface option.
(:method-arguments {form}+)
You can use this option to specify that the methods accept
18
different arguments than does the generic function itself.
By default, the methods receive the same arguments that are
specified at the top of the defgeneric form. The
:method-arguments option is only meaningful in connection
with the :inteface option.
(:generic-function-class class-name)
Specifies that this generic function has a different
implementation than the system-provided default. If you
have invented a new generic function class, this option is
the mechanism for creating a generic function of the new
class. class-name is the name of a class that is capable
of being the class of a generic function.
(:method-class class-name)
This is used to specify that all methods for this generic
function have a different implementation than the
system-provided default. If you have invented a new method
class, this option is the mechanism for creating methods of
the new class. Once the defgeneric form with this option
is evaluated or compiled, any methods you define with
defmethod will be implemented in the class name specified
here. class-name is the name of a class that is capable of
being the class of a method.
Some implementations might add other options to defgeneric. Therefore it
is required that all implementations signal an error if they observe an
option that is not implemented locally.
3.5 Accessing Slots Inside a Method: WITH-SLOTS
with-slots sets up a context giving you lexical access to the slots of one
or more instances. Alternatively, you can use the accessor functions
within the method to access slots.
(with-slots ({var-name | (var-name {keyword argument}*)}*)
19
{form}*)
If var-name is given alone, all slots of the instance can be accessed by
name. It is necessary that the class of the instance can be determined, so
var-name must be the name of a qualified parameter in the lambda-list of a
method lexically containing this with-slots form. If the class of the
instance cannot be determined that way, use the :class option.
If more than one var-name is given, then they must have disjoint sets of
names for slots to avoid ambiguity of reference, or use the :prefix option
to distinguish between the names. If any ambiguity is detected, with-slots
signals an error.
The recognized keywords and their arguments are:
:class class-name
The class, or a super class, of the instance. This option
is necessary if the class of the instance cannot be
determined from the method's lambda-list.
:prefix prefix prefix is either a symbol or a string. You can then
reference a slot by the variable named by prefix followed
by the slot name. This enables you to keep separate two
instances whose slot names overlap, such as when you want
to access two instances of the same class, or that share a
super-class.
:direct boolean If t, with-slots accesses the slots directly, using
slot-value. If nil, with-slots calls the accessor
functions, which might invoke methods.
For example:
(defclass point ((x 0) (y 0)) ()
(:accessors-with-prefix point-))
(defmethod move ((p point) dx dy)
(with-slots (p) ;; p is known as a point from the method args
(setq x (+ x dx) y (+ y dy))))
20
(defmethod move ((p point) dx dy)
(with-slots ((p :direct t))
(setq x (+ x dx) y (+ y dy))))
(defmethod make-same-height ((p1 point) (p2 point))
;;; set the y coord of p1 to the y of p2, its reference.
;;; slots of p1 are accessed using the slot names
;;; slots of p2 are accessed using ref-<slot-name>
(with-slots (p1 (p2 :prefix ref-))
(setq y ref-y)))
(defmethod make-horizontal ((l line))
(let ((left (left-point l)) (right (right-point l)))
(with-slots ((left :class point :prefix left-)
(right :class point :prefix right-))
(setq left-y right-y))))
3.6 Accessing Slots Directly: SLOT-VALUE
The usual way to access slots is to specify the :accessor or :reader option
to defclass and use the automatically-generated generic function. However,
when no reader or accessor is defined for a slot, you can use slot-value to
access the slot. setf may be used with slot-value to write the value of
the slot. This is often useful in the debugging phase; it is also used
internally.
(slot-value instance symbol [no-error-p])
instance is the instance to be examined, and symbol is the slot name. If
there is no such slot, an error is signalled, unless no-error-p is non-nil,
in which case nil is returned.
21
3.7 CALL-NEXT-METHOD
call-next-method is used within the body of a method. It is called with no
arguments.
The type of method combination in use determines which kinds of methods
allow call-next-method to be used. The default method combination type,
:daemon, allows call-next-method to be used inside primary methods and
:around methods. If you define a new type of method combination using the
simple form of define-method-combination, call-next-method can be done in
:around methods only.
(call-next-method) calls the next method with the same arguments that this
method received. For the precise definition of "next method": See the
section "Method Selection", page 40. The value or values returned by that
method are returned by the (call-next-method) form. Further computation
after call-next-method returns is possible, so this provides a general
imperative form of method-combination.
The exact definition of "same arguments" is still under discussion and must
be clarified. This is particularly complicated in the face of optional,
rest, and keyword parameters. call-next-method passes the actual values of
the parameter variables to the next method, except for keyword parameters,
which are passed via a rest-argument, not individually. Thus defaulted
optional parameters cannot be defaulted again by the next method, but
keyword parameters can be re-defaulted by the next method. If a parameter
variable is setq'ed, or another variable with the same name is bound, the
variable value visible at the lexical position of the call-next-method form
is used.
If call-next-method runs out of methods, it signals an error.
Possible extensions:
(call-next-method {argument}*)
When at least one argument is given, call-next-method
supplies those arguments instead of the arguments that this
method received. There are a lot of applications for this.
22
(apply-next-method {argument}+)
This is like call-next-method except that it uses apply
instead of funcall. That is, the last argument is really a
list of arguments.
3.8 CLASS-OF
(class-of object)
Returns the most specific class of which object is a member. Every Lisp
object has a class.
See the section "Assigning Classes to a Subset of Common Lisp Types", page
46.
3.9 DESCRIBE
The existing describe function is required to be replaced by this generic
function.
(defgeneric describe (object))
Each implementation is required to replace its former implementation of
describe with one or more methods. Exactly which classes have methods for
describe is not specified.
Users can also write methods for describe; these methods must conform to
the Common Lisp definition of describe in Common Lisp the Language. A
user-defined class without a method for describe inherits an appropriate
system-defined method.
23
3.10 PRINT-OBJECT
(defgeneric print-object (object stream))
print-object outputs the printed representation of the object onto the
stream. print-object is called by the print system, and should not be
called by the user. A user-defined class can have a method for
print-object that defines its printed representation. If it has no method,
it inherits a system-defined method which produces a default
printed-representation, perhaps using a syntax such as #<...> that is not
understood by the reader.
Each print-object method must obey the print-control special variables as
documented in Common Lisp the Language pp. 370-373. The specific details
are:
*print-escape* Each method must implement this.
*print-pretty* This can be ignored by most methods other than the one for
lists.
*print-circle* This is handled by the printer and can be ignored by
methods.
*print-level* The printer takes care of this automatically, provided that
each method handles exactly one level of structure and
calls write (or an equivalent function) recursively if
there are more structural levels. The printer's decision
of whether an object has components, and therefore should
not be printed when the printing depth is not less than
*print-level*, is implementation-dependent. In some
implementations its print-object method is not called, in
others the method is called and the determination that the
object has components is based on what it tries to output
to the stream.
*print-length* Methods that produce output of indefinite length must obey
this, but most methods other than the one for lists can
ignore it.
24
*print-base*, *print-radix*, *print-case*, *print-gensym*, and
*print-array* apply to specific types of objects and are handled by the
methods for those objects.
In general, the printer and the print-object methods should not rebind the
print-control variables as they recurse through the structure, but this is
necessarily implementation-dependent.
In some implementations the stream argument passed to a print-object method
is not the original stream, but is an intermediate stream that implements
part of the printer, so methods should not depend on the identity of this
stream.
All of the existing printing functions (write, prin1, print, princ, pprint,
write-to-string, prin1-to-string, princ-to-string, the format ~S and ~A
operations, and the format ~B, ~D, ~E, ~F, ~G, ~$, ~O, ~R, and ~X
operations when they encounter a non-numeric value) are required to be
changed to go through the print-object generic function. Each
implementation is required to replace its former implementation of printing
with one or more print-object methods. Exactly which classes have methods
for print-object is not specified; it would be valid for an implementation
to have one default method that is inherited by all system-defined classes.
References: See MIT AI Memo 816, December 1984, by Richard C. Waters for a
discussion of PP, one of the better designed Lisp printers. It handles
*print-level* and *print-length* invisibly to the print-object methods.
See also the inconclusive discussion on printing, *print-level*, and
recursion on the Common-Lisp mailing list around 21 November 1985 for
additional background.
3.11 Defining New Types of Method Combination
This section describes the tools for defining new types of method
combination. The key function is:
define-method-combination
25
Enables you to declare a new type of method combination.
The simple syntax defines a type of method combination that
calls all the methods, passing the values they return to a
given function. The extended syntax offers a rich
declarative syntax.
The following tools are often used in define-method-combination forms:
call-component-method
Produces a form that calls a component method.
call-component-methods
Produces a form that invokes a function or special form.
Each argument to that function is a call to one of the
methods in the supplied list of methods.
multiple-value-prog2
Like multiple-value-prog1 but returns all the values of the
second form.
method-options Extracts the method options portion of a method.
3.11.1 Simple Syntax of DEFINE-METHOD-COMBINATION
The simple syntax is recognized by the second argument, which is a symbol.
(define-method-combination name operator {keyword argument}*)
name is a non-null symbol, often a keyword, that is the name of the type of
method combination being defined.
operator names a function, macro, or special form. This defines a new type
of method combination in which all the methods are called and their values
are passed to operator. :around methods are supported in the types of
method combination defined by the simple syntax, and call-next-method can
be done in those :around methods. However, call-next-method cannot be done
in a primary method that uses a method combination type defined by the
simple syntax.
26
The valid keywords for the simple syntax are:
:pretty-name string
This string defaults to the lower case version of the
supplied name of the method combination type.
:single-argument-is-value boolean
Default is nil. This option is used to optimize for
operators that return their argument if there is only a
single argument, such as +, max, and and. If t is supplied
for this option, and only one method is available, the
method is called directly; operator is not called.
Method combination types defined by the simple syntax use
:most-specific-first order. However, you can override that by using the
defgeneric :method-combination option, and providing the argument
:most-specific-last after the name of the method combination type.
3.11.2 Extended Syntax of DEFINE-METHOD-COMBINATION
The extended syntax is recognized by the second argument, which is a lambda
list.
(define-method-combination name parameters ({method-pattern}*)
{options}* {form}*)
name is a non-null symbol, often a keyword, that is the name of the type of
method combination being defined.
parameters resembles the parameter list of a defmacro; it is matched
against the parameters specified in the :method-combination option to
defgeneric.
method-patterns is a list of method pattern specifications. Each method
pattern selects some subset of the available methods and binds a variable
to a list of the methods. Two of the method patterns select only a single
method and bind the variable to the chosen method if a method is found and
27
otherwise to nil. The variables bound by method patterns are lexically
available while executing the forms. See the section "Method-patterns
Option to DEFINE-METHOD-COMBINATION", page 30.
Each option is a list whose car is a keyword. These can be inserted in
front of the body forms to select special options. See the section
"Options Available in DEFINE-METHOD-COMBINATION", page 32.
The forms are evaluated to produce the body of a handler. Thus the body
forms of define-method-combination resemble the body forms of defmacro.
Backquote is used in the same way. The body forms of
define-method-combination usually produce a form that includes invocations
of call-component-method and/or call-component-methods. These functions
hide the implementation-dependent details of the calling of component
methods.
The forms are permitted to setq the variables defined by the
method-patterns, if further filtering of the available methods is required,
beyond the filtering provided by the built-in filters of the
method-patterns mechanism. It is rarely necessary to resort to this. It
is assumed that the values of the variables defined by the method patterns
(after evaluating the body forms) reflect the actual methods that will be
called.
forms must not signal errors.
3.11.3 Examples of DEFINE-METHOD-COMBINATION
The examples in this section are presented as illustration of the
complicated syntax of define-method-combination. Note that the :daemon
type definition here does not allow for :around methods.
This form defines the :daemon method-combination type:
(define-method-combination :daemon
(&optional (order ':most-specific-first))
;; select methods and bind them to variables
28
((before "before" :every :most-specific-first (:before))
;; select primary method,
;; if none is present, select :default method
(primary "primary" :first order () :default)
(after "after" :every :most-specific-last (:after)))
;;return values from primary method
`(multiple-value-prog2
,(call-component-methods before)
,(call-component-method primary)
,(call-component-methods after)))
This form defines the :two-pass method combination type:
(define-method-combination :two-pass
(&optional (order ':most-specific-first))
((first "first-pass" :every order () :default)
(second "after" :every :most-specific-last (:after)))
;;return values from last primary method to run
`(multiple-value-prog1
,(call-component-methods first)
,(call-component-methods second)))
This form defines the :inverse-list method combination type:
(define-method-combination :inverse-list () ;take no parameters
;; select methods of type :inverse-list or :default
((methods "inverse-list" :every :most-specific-first
() (:inverse-list) :default))
(:arglist ignore list)
(:method-transformer
;; each method receives a single argument, regardless
(:generic-method-arglist `(list-element)))
`(let ((list ,list))
,@(loop for (method . rest) on methods
collect (call-component-method method
:arglist `(,(first list)))
when rest
collect `(setq list (cdr list)))))
29
3.11.4 Method-patterns Option to DEFINE-METHOD-COMBINATION
Each method-pattern is a list of the form:
(variable printer filter order {pattern}*)
variable is a variable to be bound to the list of methods resulting from
the application of this method-pattern (or to a single method or nil, in
the case where filter is one of the symbols :first or :last). Each pattern
selects some subset of the methods (multiple patterns are combined with
or). The order specifies the ordering of this subset and the filter
specifies further pruning of it.
printer is a format control string for describing this method concisely.
format is given this string and the method options as arguments.
filter is not evaluated; the filter type cannot be variable at run time.
filter must be one of the following symbols:
:first Selects the first method in the specified order (nil if
there are no methods).
:last Selects the last method in the specified order (nil if
there are no methods).
:every Selects all the methods.
:remove-duplicates
Selects all of the methods, excluding any duplicate
methods. If duplicates are present, only the first method
is selected. Duplicate methods are detected by applying
equal to their defmethod options.
Additional filter types might be added in the future. You can also do your
own filtering in the body by using setq on a variable.
order is a form that must evaluate to :most-specific-first or
:most-specific-last. Often it is simply one of those keywords, a
30
self-evaluating constant. Another common practice is for order to be one
of the variables in parameters.
Each pattern is a list or (). A method matches the pattern if its options
in the defmethod form match the given pattern. The pattern () matches
methods with no options; that is, primary methods. If you do not specify
any patterns, a default pattern of () is assumed. patterns may be dotted
lists.
Match is by equal, except that * matches anything. Each * in a pattern
matches anything in that position of a method function spec. Dotted *
might be useful for variable length.
You can intermix special symbols with the patterns. The only such symbol
currently allowed is:
:default If the other patterns find any methods, this is ignored.
If no other methods are found, the methods matched by the
pattern (:default) are selected.
method-patterns are considered sequentially in the order they are written.
The first method-pattern that matches a component method with its patterns
takes care of that method, either including it or rejecting it depending on
its filter and any :default processing specified. Subsequent
method-patterns do not see that method. This means you should put the most
general patterns last if more than one method-pattern clause could match
the same method. The methods are expected to be called in the order the
method patterns are written. If this is not so, you should include an
:order clause. See the section "Options Available in DEFINE-METHOD-
COMBINATION", page 32.
Any methods not taken care of by any method patterns are extraneous, and
implementations should warn about them.
Here is an example of the method-patterns used by :case method combination
type:
((case-documentation "case-documentation" :first order (:case-documentation
(which-operations "which-operations" :first order (:which-operations))
31
(otherwise "otherwise" :first order (:otherwise))
(cases "case ~s" :remove-duplicates order (*)))
3.11.5 Options Available in DEFINE-METHOD-COMBINATION
The options to define-method-combination include:
(:arglist {arg}*)
Specifies the argument list of the generic function. As the body
forms are being executed, each variable in args is bound to a form
that accesses the relevant argument when evaluated as part of the
form returned by the body forms. &optional and &rest are permitted
in args.
The :arglist option is useful when the action depends on the
arguments (rather than simply passing the arguments on to the
component methods), as in :case or :inverse-list method combination.
See the section "Examples of DEFINE-METHOD-COMBINATION", page 28.
(:order {var}*)
Specifies the order in which the component methods will be called,
for the benefit of flavor examining tools. Each of the vars must be
a variable bound by one of the method-patterns. Normally all of the
vars bound by method-patterns would be included in the :order
option, but this is not required. If no order is specified, the
default is to use the vars in the order in which the method-patterns
are written.
(:method-transformer {clause}*) can make changes to the arguments and body
of methods for generic functions that use this type of method
combination, and can control the validation of the arguments in the
defmethod against the arguments in the defgeneric. Code in clauses
receives arguments bound to the following variables, and also has
the parameters of the define-method-combination available to it:
method The name of the method
32
method-combination
The method-combination type
method-arglist
The arglist specified for the method
method-body
The body of the method, including declarations
generic-method-arglist
The arglist specified for methods in the defgeneric.
Each clause consists of a keyword and a form evaluated to produce a
replacement for a normal item. The clauses are:
:method-arglist
Replaces the argument list specified in defmethod.
:method-body
Replaces the body specified in defmethod.
:generic-method-arglist
Replaces the argument list for methods specified in
defgeneric.
:inhibit-checking
If non-nil, inhibits comparing method-arglist and
generic-method-arglist for the wrong number of arguments.
3.11.6 CALL-COMPONENT-METHOD
(call-component-method method {keyword value}*)
Produces a form that calls method. If no keyword arguments are given to
call-component-method, the method receives the same arguments that the
generic function received. Additional internal arguments might be passed
to the method, but the user never needs to be concerned about these.
33
The recognized keywords are :apply and :arglist.
The value of :arglist is a list of forms to be evaluated to supply the
arguments to the method, instead of simply passing through the arguments to
the generic function.
When :arglist and :apply are both supplied, :apply should be followed by t
or nil. If :apply t is supplied, the method is called with apply instead
of funcall. :apply nil causes the method to be called with funcall.
When :arglist is not supplied, the value following :apply is the argument
that should be given to apply when the method is called. (Certain internal
arguments are also included in the apply form.) For example:
(call-component-method method :apply list)
Results in:
(apply method :apply list)
In other words, the following two forms have the same effect:
(call-component-method method :apply list)
(call-component-method method :arglist (list list)
:apply t)
If method is nil, call-component-method produces a form that returns nil
when evaluated.
For examples: See the section "Examples of DEFINE-METHOD-COMBINATION",
page 28.
3.11.7 CALL-COMPONENT-METHODS
(call-component-methods ({method}*) [:operator operator])
Produces a form that invokes the function or special form named operator.
34
operator defaults to progn. Each argument or subform is a call to one of
the methods.
3.11.8 MULTIPLE-VALUE-PROG2
(multiple-value-prog2 {form}*)
Evaluates the forms and returns all the values of the second form. This is
similar to multiple-value-prog1.
3.11.9 METHOD-OPTIONS
(method-options method)
method-options returns the method's options, which is the options argument
that was given in the defmethod form for this method, a list of symbols
and/or numbers, such as :before or :around.
35
4. BASIC UNDERLYING MECHANISMS
This section describes some important underlying mechanisms, including the
determination of a class precedence list, the selection and sorting of
available methods, and the final process of combining those methods and
executing them.
4.1 Determining the Class Precedence List
The method selection process requires that a class precedence list be
computed, which is an ordering of all components of that class from most
specific to least specific.
In the simplest case, suppose the default method combination (:daemon with
:most-specific-first order) is used. Suppose two components of the class
supply a primary method for the same generic function and no other methods
(such as :before, :after, or :around) are available. The method selection
process must choose one of the two primary methods to run; the method
supplied by the most specific class in the class precedence list is chosen.
For other illustrations of how the method selection process depends on the
class precedence list: See the section "Method Selection", page 40.
The defclass forms of a class and each of its super-classes set local
constraints on the ordering of classes. When a class is built from
super-classes, all of the local constraints of the class and its
super-classes are taken into account, and an ordering is computed that
satisfies all of these constraints. In other words, the defclass forms
specify partial orderings, which must be merged into one total ordering.
Three rules govern the ordering of classes:
1. A class always precedes its own super-classes.
2. The local ordering of super-classes within each defclass form is
preserved.
36
3. Duplicate classes are eliminated from the ordering; if a class
appears more than once, it is placed as close to the beginning
of the ordering as possible, while still obeying the other
rules.
The first step in merging the partial orderings is to construct a tree of
classes. The root of the tree is the class for which we are computing a
class precedence list. From that root, the class's super-classes are added
from left to right, as they appear in the defclass form.
The ordering is determined by walking through the tree in depth-first
left-to-right order, and adding each class to the ordering if it fits the
constraints of the three rules. If the class can be placed next in the
ordering without transgressing one of the three rules, it is added. If
adding it would transgress a rule, that class is skipped. If the end of
the tree is reached and some classes have not yet been placed in the
ordered list, we walk through the tree again, continuing to apply the three
rules to the remaining classes and add them to the ordering. In
complicated programs, it is conceivable that we could walk through the tree
several times.
If some classes cannot be ordered according to the rules, an error is
signalled to inform the user of the conflicting constraints. An example of
this is given at the end of this section.
Example of A Tree Walk
Here is an example of determining a class precedence list. The classes are
defined:
(defclass pie (apple cinnamon) ())
(defclass apple (fruit) ())
(defclass cinnamon (spice) ())
(defclass fruit (food) ())
(defclass spice (food) ())
(defclass food () ())
The tree of classes for pie is:
37
pie
/ \
apple cinnamon
/ \
fruit spice
/ \
food food
The list begins with pie. We continue, adding apple and fruit. So far,
the (incomplete) ordered list is:
(pie apple fruit
The next class is food, but we cannot place it next in the ordering because
doing so would transgress the rule that a class always precedes its own
super-classes. If placed next, food would precede spice, and we know that
spice must precede food. Thus we skip food and continue through the tree.
Because food appears later in the tree, we pick it up then. The ordering
is now complete:
(pie apple fruit cinnamon spice food)
Several Possible Orderings
In many cases, the three rules define a single valid ordering of classes.
In other cases, several orderings are valid. For example, suppose pie
class and its super-classes are defined as follows:
(defclass pie (apple cinnamon) ())
(defclass apple (fruit) ())
(defclass cinnamon (spice) ())
(defclass fruit () ())
(defclass spice () ())
The tree-walk results in the ordering:
(pie apple fruit cinnamon spice)
In fact, there are two additional valid orderings:
38
(pie apple cinnamon fruit spice)
(pie apple cinnamon spice fruit)
A well-conceived program must not depend on any one of those orderings, but
should work equally well under any of them. For example, if your program
depends on spice preceding fruit in the ordering for pie, you should make
that constraint explicit by including those two classes in the list of
super-classes in the defclass form for pie.
Conflicting Class Definitions
It is possible to write a set of class definitions that cannot be ordered
using the rules. For example:
(defclass new-class (fruit apple) ())
(defclass apple (fruit) ())
fruit must precede apple because the local ordering of super-classes is
preserved. apple must precede fruit because a class always precedes its
own super-classes. When this situation occurs, an error is signalled when
the system tries to compute the class precedence list. At that point you
can redefine one or more of the classes to resolve the problem, presumably
by changing the local order of super-classes to resolve the conflict.
Note the following example, which appears at first glance to be a
conflicting set of definitions:
(defclass pie (apple cinnamon) ())
(defclass pastry (cinnamon apple) ())
The ordering for pie is:
(pie apple cinnamon)
The ordering for pastry is:
(pie cinnamon apple)
There is no problem with the fact that apple precedes cinnamon in the
39
ordering of the super-classes of pie, but not in the ordering for pastry.
However, you cannot build a new class that has both pie and pastry as
super-classes .
4.2 Method Selection
Each class inherits methods from its components. That is, if a method is
defined for a component of a class, that method is automatically considered
to "be available" for this class. Depending on the other methods that are
available and the type of method combination used, that method might or
might not ever be used for this class.
Before describing the general mechanism, we present a simple case. Class A
has a single component, Class B. Both Class A and B supply a primary method
for the same generic function. The generic function dispatches on only one
argument; these are classical methods. In :daemon method combination, a
single primary method is chosen from the set of available methods. When
the argument to the generic function is an instance of Class A, the set of
available methods includes both methods. The method chosen is the method
that is written for the most specific class; that is the method for Class
A.
The method for Class B is said to be "shadowed". If the method definition
for Class A is removed, the method for Class B will be used for instances
of Class A.
This behavior enables a more specific class to override methods inherited
from its components. It also allows a class to use an inherited method,
but modify it somewhat. For example, a class can inherit a primary method
for a generic function and also supply a :before or :after method.
The same principles apply for multi-methods. The general algorithm for
method inheritance is:
1. Select the set of available methods.
40
Given a generic function and the arguments to it, the set of available
methods includes all methods for that generic function whose qualified
parameters match the arguments. A qualified parameter matches its argument
if the class of the argument has the class in the qualifier as a component
class.
2. Sort the methods into a precedence order.
Examine the arguments supplied to the generic function one at a time, from
left to right. (The left-to-right precedence order is the default. It can
be changed by the :argument-precedence-order option to defgeneric.) For
each argument to the generic function, compare the corresponding parameter
type specifier of the methods. Any unqualified parameters have an implied
type specifier of t.
If the type specifiers are not equal, you can determine which method has
precedence by the following rule: Method X is more specific than method Y
if method X's parameter type specifier is earlier than method Y's parameter
type specifier in the class precedence list of the class of the argument.
t is implied at the very end of each class precedence list, so it is less
specific than any other class.
This algorithm is a function of the actual values of the arguments supplied
to the generic function, but in practice an implementation will not execute
the entire algorithm every time the generic function is called. Most
implementations will normally precompute enough information that they can
make some quick tests on the classes of some of the arguments, perhaps
using hash tables, and get to the appropriate method as soon as possible.
Once the available methods are sorted into a precedence list, the type of
method combination decides which methods are to be executed, and what will
be done with their values.
41
4.3 Method Combination
The default type of method combination is called :daemon. This allows for
primary methods, and multiple :before and :after methods. It also allows
for :around methods and call-next-method.
A method's type is determined by the option argument to defmethod. The
following method types are recognized by :daemon method combination:
primary These methods have no options in the defmethod form. They
are the most common type of method.
:default This type of method acts as a primary method if no other
primary method is available. If a primary method is
available, :default methods are ignored. (These are not to
be confused with "default methods", which are methods that
are not type-qualified, or are type-qualified with t as the
type.)
:before :before methods have the keyword :before as the defmethod
option. They are used to specify code that is to run
before the primary method.
:after :after methods have the keyword :after as the defmethod
option. They are used to specify code that is to run after
the primary method.
:around :around methods have the keyword :around as the defmethod
option. :around methods offer an advanced capability that
is rarely needed by most programmers. Inside the body of
an :around method, the function call-next-method can be
used to immediately call the "next method" (see below);
when the next method returns, the :around method can
execute more code, perhaps based on the returned value(s).
The semantics of :daemon method combination are:
- If there are any :around methods, the most specific :around
42
method is executed, and supplies the value(s) of the generic
functioon.
- If an :around method uses call-next-method, the next most
specific :around method is executed, if one is available.
- If there are no :around methods at all, or if call-next-method is
done by the least specific :around method, the other methods are
executed in the following way:
* All the :before methods are executed, in most-specific-first
order.
* The most specific primary method is executed, and supplies
the value(s) of the generic function. If it does
call-next-method, the second most specific primary method is
executed, and so on until there are no more primary methods.
An error is signalled if call-next-method is used and there
is no primary method available to call.
* All the :after methods are executed in most-specific-last
order.
Special Notes:
If there is at least one :before or :after method, but no primary method, a
primary method that ignores its arguments and returns nil is assumed.
It should be noted that all :around methods run before any primary methods
run. Thus a less specific :around method runs before a more specific
primary method. This behavior might seem to violate modularity principles.
:around methods are included in the specification because there is a
documented history of a need for such a feature.
This section has described the use of the default :daemon method
combination type, and the default order, which is :most-specific-first.
You can use the :method-combination option to defgeneric and supply an
argument of :most-specific-last to change the order of methods.
:most-specific-last reverses the order of the primary methods and reverses
43
the order of the :around methods. The order of the :before and :after
methods is not changed, and it is still the case that all of the :around
methods are executed before any primary methods.
44
5. DESIGN RATIONALE
This section discusses the design rationale behind some issues, and
mentions possible extensions to the Classes standard.
5.1 Motivation for Several DEFCLASS Defaults
In designing defclass we followed the principle that the definer of a class
should be able to decide which parts of the class are strictly internal,
and which are intended as an external interface. This design principle is
a guideline that encourages good data abstraction.
Therefore, all defclass options that make a slot visible (such that its
value could be read, written, or initialized) are turned off by default.
The definer of a class needs to make an explicit decision to make a slot
visible externally, by specifying one or more options.
Along the same lines, the :accessor-prefix and :reader-prefix options do
not have a default for the prefix. This represents a departure from the
analogous defstruct :conc-name option, which does have a default behavior,
the name of the structure followed by a hyphen.
If the definer of the class uses the :accessor-prefix or :reader-prefix
option, the slots are part of the external interface. There are several
reasonable conventions for naming slot accessors and readers. The definer
of the class might choose "" as the prefix, or the name of the class, a
more general component, or the name of a protocol followed by a hyphen.
Since no single possibility is clearly better than the others, we provide
no default prefix and leave the decision to the definer of the class.
45
5.2 Assigning Classes to a Subset of Common Lisp Types
Many of the predefined Common Lisp type specifiers have a class associated
with them. For example, an array is of type array, and of class array. A
class that corresponds to a predefined Common Lisp type specifier is called
a built-in class.
Every class has a corresponding type. However, not all types have a
corresponding class. This section presents the mapping of classes to types
and explains the rationale for excluding some types.
Users can write methods that dispatch on any primitive Lisp type that has a
corresponding class. However, it is not allowed to make an instance of a
built-in class with make-instance, nor to include a built-in class as a
super-class of a class.
The difference between built-in classes and user-defined classes is in the
implementation of instances. Instances of a built-in class are implemented
in a specialized way that does not permit subclassing. In some
implementations some classes documented as built-in might in fact be
implemented as user-defined classes, but portable programs cannot assume
this.
A type-specifier in a qualified-variable in a defmethod lambda-list must be
a member of a limited subset of Common Lisp type-specifiers. It would be
useful to extend this to the full generality of Common Lisp
type-specifiers, but this raises technical issues that are not yet
appropriate for standardization. For example, deciding what to do when an
argument is an instance of two types, both of which have methods, but
neither is a subtype of the other. Another issue is the question of the
reliability of subtypep in some implementations. Dealing with not raises
yet another technical issue. For more information, see Boolean Classes by
D. McAllester and R. Zabih, a paper presented at the 1986 ACM First Annual
Conference on Object-Oriented Programming Systems, Languages, and
Applications.
Currently acceptable type-specifiers must be symbols (no lists are
permitted) in one of the following four categories:
46
1. t, the universal class.
2. The name of a user-defined class, defined with defclass.
3. The name of a structure, defined with defstruct without using :type.
4. One of the following built-in class names:
array
bit-vector subclass of vector, array, sequence
character
compiled-function
complex subclass of number
cons subclass of list, sequence
double-float subclass of float, number
float subclass of number
hash-table
integer subclass of rational, number
list subclass of sequence
long-float subclass of float, number
null subclass of symbol, list, sequence
number
package
pathname
random-state
ratio subclass of rational, number
rational subclass of number
readtable
sequence
short-float subclass of float, number
single-float subclass of float, number
string subclass of vector, array, sequence
symbol
vector subclass of array, sequence
Converting a partial ordering to a total ordering for the sake of brevity,
classes are ranked here in order from most specific to most general:
rational float number symbol list vector array sequence
47
List of Types that have no Corresponding Class
The following types could have been allowed as classes, but we have
excluded them for the reasons shown:
- Types too specific to be useful to put methods on: bit, keyword,
standard-char, string-char
- The type implies an implementation, not behavior: bignum,
fixnum, simple-array, simple-bit-vector, simple-string,
simple-vector
- Specification of the type is too vague: common, function, stream
- This is the same as (not cons), and we are not yet dealing with
not: atom
- No object can be an instance of this type: nil
The following types are also excluded as classes: add, member, mod, not,
or, satisfies, values.
Another way of looking at these choices is that a type is included only if
there is a function for making objects of this type, such as make-array.
bit, fixnum, and bignums are excluded because there are no separate
functions for making objects of those types. function is excluded because
there is no function to make functions, but compiled-function is included
because the compile function exists. stream is excluded because there is
no single function to make streams, and in fact many different types of
objects can be streams. This line of reasoning is not watertight, but it's
a pretty good heuristic.
Individual implementations can extend this to allow other type-specifiers.
Also individual implementations can add additional subclass relationships
as long as they do not violate Common Lisp the Language pp.33-5. For
example, readtable can be a subclass of array, and hence inherit methods
from array, in some implementations. It is important that the subclass
relationships among built-in classes be identical with the subtype
relationships among their corresponding types, so that the class system is
merged seamlessly with the type system.
48
5.3 Why DEFMETHOD Disallows Method Selection on Optional Parameters
Currently defmethod only allows required parameters to have qualifiers and
be used for method selection. It would be useful to extend defmethod to
allow optional and keyword parameters to be used for method selection. (It
does not seem useful to allow rest, supplied-p, or auxiliary parameters to
be used for method selection.) This extension has not been included in the
present standard because it raises technical issues about defaulting,
consistency among methods, and order of evaluation of initforms.
Discussion among the group has revealed that these issues are not well
enough understood yet to be appropriate for standardization.
Note that the effect of using an optional or keyword parameter for method
selection, from the point of view of the caller of the generic function,
can be achieved by using the :interface option to defgeneric to turn the
optional or keyword parameter of the generic function into a required
parameter of the methods. Thus all defaulting and order of evaluation
issues are centralized within the defgeneric, at the cost of requiring the
writer of a method to specify a different parameter list.
5.4 Method Selection by Predications More General Than Classes
This section should be considered a possible extension to the standard. It
is included in "Design Rationale" because it has been discussed in the
group.
The standard as currently proposed only allows method selection by the
classes of the arguments. This could be extended in various ways. It is
important that any extensions to method selection remain consistent with,
and a subset of, Common Lisp type-specifiers, rather than introducing a
whole new type system in parallel with the existing one.
Method selection could be extended to allow the full generality of Common
Lisp type-specifiers; that is, arbitrary predications. The problem with
this is that there can be predications that have objects in common, but do
49
not have a subtype/supertype relationship. Thus if methods are defined for
both predications, it is not clear which method has precedence. This is in
contrast to classes; if an object is an instance of two classes, one class
is always a component of the other, and the subtype/supertype relationship
can always be determined.
Rather than totally ruling out method selection by predications because it
might not be clear which method has precedence, predications could be
allowed, with the requirement that an error is signalled if such a method
ambiguity is actually encountered. This would not prevent users from
defining a method on one predication, but would preclude defining methods
on two overlapping predications such as (integer 1 100) and (integer 50
144), which would signal an error if and only if the argument was an
integer between 50 and 100.
Subtype/supertype relationships can be computed for many of the most useful
predications, but in the most general case where this ordering is not
computable, the semantics are still well-defined, but not as amenable to
optimization. At run-time the value of the argument must be tested against
both predications. If both are true an error must be signalled, otherwise
the applicable method is well-defined. This extra testing of predications
can be minimized by converting the type-specifiers to canonical form. The
details are not given here because this is evidently an unexplored area
that is not yet appropriate for standardization.
50
6. FUTURE DIRECTIONS
Our goal is to write a complete specification of the standard, which will
include all information necessary to implement Classes. We invite feedback
on this initial draft from the community now, and will continue to invite
feedback as the specification process continues.
You can send comments on the ideas in this document to the mailing list:
Common-Lisp-Classes-discussion@MC.LCS.MIT.EDU
You can request a copy of this document by sending a message to:
Common-Lisp-Classes-document-request@Stony-Brook.SCRC.Symbolics.COM
As initially proposed, the standard was meant to fulfill two primary goals.
First, to define a basic set of tools for object-oriented programming.
Second, to define a flexible framework in which exploration of other
paradigms of object-oriented programming could take place. These two goals
are conceptually distinct; the first addresses an immediate need, while the
second addresses a long-term strategy for continued experimentation toward
resolving some research areas.
Since the key ideas underlying the programming interface are already
well-understood and tested (in Flavors and CommonLoops), we expect that
this will be the quickest and simplest part of the standard to define. We
hope to make a proposal for the programmer interface available as soon as
possible, in advance of the proposal for the flexible framework. Users
could start writing programs in terms of the programmer interface sooner,
since they would not have to wait for the complete standard to be
finalized.
We intend to adapt the Meta-object protocol from CommonLoops for the
flexible framework, but it has neither been documented nor discussed within
the working group. It is therefore premature to document the Meta-object
protocol at this time.
It is hoped that a portable implementation of Classes will be written, to
51
allow the community to experiment with the new ideas and gain a deeper
understanding of Classes than would be possible by reading the
specification alone.
52
INDEX
53
TABLE OF CONTENTS
Page
1. STATEMENT OF GOALS 1
2. TERMINOLOGY 4
3. PROGRAMMER INTERFACE 7
3.1 Defining Classes 7
3.2 Making a New Instance 13
3.3 Defining Methods and SETF Methods 14
3.4 Defining Generic Functions 17
3.5 Accessing Slots Inside a Method: WITH-SLOTS 19
3.6 Accessing Slots Directly: SLOT-VALUE 21
3.7 CALL-NEXT-METHOD 22
3.8 CLASS-OF 23
3.9 DESCRIBE 23
3.10 PRINT-OBJECT 24
3.11 Defining New Types of Method Combination 25
3.11.1 Simple Syntax of DEFINE-METHOD-COMBINATION 26
3.11.2 Extended Syntax of DEFINE-METHOD-COMBINATION 27
3.11.3 Examples of DEFINE-METHOD-COMBINATION 28
3.11.4 Method-patterns Option to DEFINE-METHOD-COMBINATION 30
3.11.5 Options Available in DEFINE-METHOD-COMBINATION 32
3.11.6 CALL-COMPONENT-METHOD 33
3.11.7 CALL-COMPONENT-METHODS 34
iii
3.11.8 MULTIPLE-VALUE-PROG2 35
3.11.9 METHOD-OPTIONS 35
4. BASIC UNDERLYING MECHANISMS 36
4.1 Determining the Class Precedence List 36
4.2 Method Selection 40
4.3 Method Combination 42
5. DESIGN RATIONALE 45
5.1 Motivation for Several DEFCLASS Defaults 45
5.2 Assigning Classes to a Subset of Common Lisp Types 46
5.3 Why DEFMETHOD Disallows Method Selection on Optional 49
Parameters
5.4 Method Selection by Predications More General Than Classes 49
6. FUTURE DIRECTIONS 51
INDEX 53
iv